﻿using System;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.Net.Sockets;
using System.Text;
using System.IO;
using System.Collections.Generic;
using System.Xml.Serialization;
using System.Windows.Browser;
using ClassRoom.Packets;
using Newtonsoft.Json;

namespace ClassRoom
{
    public enum ObjectType
    {
        UserInfo,
        UserInRoom,
        DisconnectUser,
        ChatMessage,
        Webcam,
        Voice,
        BlackBoard,
        FullSlide,
        OneSlide,
        PPTSlidesInfo
    }

    public class Network
    {
        public delegate void dlgConnectCompleted(object sender, SocketAsyncEventArgs e);
        public event dlgConnectCompleted OnConnectCompleted;
        public delegate void dlgReceiveCompleted(byte[] message);
        public event dlgReceiveCompleted OnReceiveCompleted;
        private Socket socket;
        byte[] data;
        int total = 0;

        public Network()
        {

        }

        public void Connect(string ip)
        {
            try
            {
                if ((socket != null) && (socket.Connected == true)) socket.Close();
            }
            catch (Exception err)
            {
                //AddMessage("ERROR: " + err.Message);
            }

            IPEndPoint endPoint = new IPEndPoint(IPAddress.Parse(ip), 4530);
            socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

            SocketAsyncEventArgs args = new SocketAsyncEventArgs();
            args.UserToken = socket;
            args.RemoteEndPoint = endPoint;
            args.Completed += new EventHandler<SocketAsyncEventArgs>(OnSocketConnectCompleted);
            socket.ConnectAsync(args);
        }

        void OnSocketConnectCompleted(object sender, SocketAsyncEventArgs e)
        {
            if (socket.Connected)
            {
                e.Completed -= new EventHandler<SocketAsyncEventArgs>(OnSocketConnectCompleted);
                e.Completed += new EventHandler<SocketAsyncEventArgs>(OnGetSize);
                byte[] buffer = new byte[4];
                e.SetBuffer(buffer, 0, buffer.Length);
                socket.ReceiveAsync(e);
                if (OnConnectCompleted != null)
                    OnConnectCompleted(sender, e);
            }
        }

        void OnGetSize(object sender, SocketAsyncEventArgs e)
        {
            lock (e)
            {
                try
                {
                    if (e.BytesTransferred == 0)
                    {
                        //Disconnect from server
                        try
                        {
                            socket.Close();
                        }
                        catch
                        {
                        }
                        return;
                    }

                    e.Completed -= new EventHandler<SocketAsyncEventArgs>(OnGetSize);
                    e.Completed += new EventHandler<SocketAsyncEventArgs>(OnSocketReceiveCompleted);

                    byte[] bufferDataSize = new byte[4];
                    Array.Copy(e.Buffer, 0, bufferDataSize, 0, 4);
                    int datasize = BitConverter.ToInt32(bufferDataSize, 0);
                    data = new byte[datasize];
                    total = 0;
                    byte[] response;
                    int surplusBytes = e.Buffer.Length - 4;
                    if (e.Buffer.Length > 4)
                    {
                        response = new byte[datasize + surplusBytes];
                        Array.Copy(e.Buffer, 4, response, 0, surplusBytes);
                    }
                    else
                    {
                        response = new byte[datasize];
                    }

                    e.SetBuffer(response, surplusBytes, response.Length - surplusBytes);
                    socket.ReceiveAsync(e);
                }
                catch (Exception ex)
                {
                    throw ex;
                }
            }
        }

        void OnSocketReceiveCompleted(object sender, SocketAsyncEventArgs e)
        {
            lock (e)
            {
                // lay kich thuoc
                if (e.BytesTransferred == 0)
                {
                    //Disconnect from server
                    try
                    {
                        socket.Close();
                    }
                    catch
                    {
                    }
                    return;
                }

                try
                {
                    int length = e.BytesTransferred;
                    bool isSurplus = false;
                    if (length > data.Length - total)
                    {
                        isSurplus = true;
                        length = data.Length - total;

                        //Array.Copy(e.Buffer, 0, data, total, length);
                    }
                    //else
                    //{

                    //}

                    // Array.Copy(e.Buffer, e.Offset, data, total, e.BytesTransferred - total);
                    // total += e.BytesTransferred;
                    total += length;
                    if (total == data.Length)
                    {
                        Array.Copy(e.Buffer, 0, data, 0, total);

                        e.Completed -= new EventHandler<SocketAsyncEventArgs>(OnSocketReceiveCompleted);
                        e.Completed += new EventHandler<SocketAsyncEventArgs>(OnGetSize);

                        byte[] response;
                        int surplusBytes = 0;
                        if (isSurplus)
                        {
                            surplusBytes = e.BytesTransferred - length;
                            //response = new byte[surplusBytes + 4];
                            response = new byte[surplusBytes];
                            Array.Copy(e.Buffer, length, response, 0, surplusBytes);
                        }
                        else
                        {
                            response = new byte[4];
                        }

                        e.SetBuffer(response, 0, response.Length);
                        socket.ReceiveAsync(e);
                        //e.SetBuffer(response, surplusBytes, response.Length - surplusBytes);
                        //socket.ReceiveAsync(e);

                        if (OnReceiveCompleted != null)
                            OnReceiveCompleted(data);

                        return;
                    }
                    else
                    {
                        e.SetBuffer(e.Buffer, total, e.Buffer.Length - total);
                    }
                }
                catch (Exception ex)
                {
                    throw ex;
                }

                socket.ReceiveAsync(e);
            }
        }

        public void Send(string message)
        {
            if ((socket == null) || (socket.Connected == false))
            {
                //AddMessage("ERROR: Not connected.");
                return;
            }

            SocketAsyncEventArgs args = new SocketAsyncEventArgs();

            byte[] lengthData = BitConverter.GetBytes(message.Length);
            byte[] messageData = Encoding.UTF8.GetBytes(message);

            byte[] messageTotal = new byte[lengthData.Length + messageData.Length];
            Array.Copy(lengthData, 0, messageTotal, 0, lengthData.Length);
            Array.Copy(messageData, 0, messageTotal, 4, messageData.Length);
            args.SetBuffer(messageTotal, 0, messageTotal.Length);
            // Send the message. 
            socket.SendAsync(args);
        }

        public void Send(object obj, ObjectType t)
        {
            if ((socket == null) || (socket.Connected == false))
            {
                return;
            }

            string output = JsonConvert.SerializeObject(obj);
            SocketAsyncEventArgs args = new SocketAsyncEventArgs();
            byte[] objData = Encoding.UTF8.GetBytes(output);
            byte[] dataType = BitConverter.GetBytes((int)t);
            byte[] messageData = new byte[4 + objData.Length];
            Array.Copy(dataType, messageData, 4);
            Array.Copy(objData, 0, messageData, 4, objData.Length);
            byte[] lengthData = BitConverter.GetBytes(messageData.Length);

            byte[] messageTotal = new byte[lengthData.Length + messageData.Length];
            Array.Copy(lengthData, 0, messageTotal, 0, lengthData.Length);
            Array.Copy(messageData, 0, messageTotal, 4, messageData.Length);
            args.SetBuffer(messageTotal, 0, messageTotal.Length);
            
            // Send the message. 
            lock (socket)
            {
                socket.SendAsync(args);
            }
        }
    }
}
